package main

import (
	"encoding/json"
	"fmt"
	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/container"
	"fyne.io/fyne/v2/dialog"
	"fyne.io/fyne/v2/layout"
	"fyne.io/fyne/v2/storage"
	"fyne.io/fyne/v2/widget"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
)

type softwareSetting struct {
	// these items are used for global settings load
	SourcePath        string `json:"sourcePath"`
	TargetPath        string `json:"targetPath"`
	OverrideOutput    bool   `json:"overrideOutput"`
	SourceClearFlag   bool   `json:"sourceClearFlag"`
	OutputFormat      string `json:"outputFormat"`
	OutputAudioFormat string `json:"outputAudioFormat"`
	OutputNameFormat  string `json:"outputNameFormat"`
	OnlyAudio         bool   `json:"only_audio"`
	Bitrate           int64  `json:"bitrate"`
	DiskProtect       int64  `json:"diskProtect"`
}

var cp string

var (
	sourcePathEntry             = &widget.Entry{}
	targetPathEntry             = &widget.Entry{}
	outputNameFormatEntry       = &widget.Entry{}
	bitrateEntry                = &widget.Entry{}
	diskProtectEntry            = &widget.Entry{}
	overrideOutputCheck         = &widget.Check{}
	sourceClearFlagCheck        = &widget.Check{}
	onlyAudioCheck              = &widget.Check{}
	outputFormatRadioGroup      = &widget.Select{}
	outputAudioFormatRadioGroup = &widget.Select{}
)

func (s *softwareSetting) load() {
	err := s.loadFromFile(cp)
	if err != nil {
		fyne.LogError("SoftwareSetting load error:", err)
		return
	}

	// ui回显
	sourcePathEntry.SetText(s.SourcePath)
	sourcePathEntry.Refresh()
	targetPathEntry.SetText(s.TargetPath)
	targetPathEntry.Refresh()
	if s.OutputNameFormat == "" {
		s.OutputNameFormat = "$[page]-$[part]"
	}
	outputNameFormatEntry.SetText(s.OutputNameFormat)
	outputNameFormatEntry.Refresh()
	overrideOutputCheck.SetChecked(s.OverrideOutput)
	overrideOutputCheck.Refresh()
	sourceClearFlagCheck.SetChecked(s.SourceClearFlag)
	sourceClearFlagCheck.Refresh()
	onlyAudioCheck.SetChecked(s.OnlyAudio)
	onlyAudioCheck.Refresh()
	if s.OnlyAudio {
		outputFormatRadioGroup.Disable()
		outputAudioFormatRadioGroup.Enable()
	} else {
		outputFormatRadioGroup.Enable()
		outputAudioFormatRadioGroup.Disable()
	}
	if s.OutputFormat != "" {
		outputFormatRadioGroup.SetSelected(s.OutputFormat)
	} else {
		outputFormatRadioGroup.SetSelectedIndex(0)
	}
	if s.OutputAudioFormat != "" {
		outputAudioFormatRadioGroup.SetSelected(s.OutputAudioFormat)
	} else {
		outputAudioFormatRadioGroup.SetSelectedIndex(0)
	}
	if s.Bitrate == 0 {
		s.Bitrate = 100
	}
	bitrateEntry.SetText(strconv.FormatInt(s.Bitrate, 10))
	if s.DiskProtect == 0 {
		s.DiskProtect = 10
	} else if s.DiskProtect == -1 {
		s.DiskProtect = 1
	}
	diskProtectEntry.SetText(strconv.FormatInt(s.DiskProtect, 10))
}

func (s *softwareSetting) loadFromFile(path string) error {
	file, err := os.Open(path)
	if err != nil {
		if os.IsNotExist(err) {
			err := os.MkdirAll(filepath.Dir(path), 0700)
			if err != nil {
				return err
			}
			err = s.save()
			if err != nil {
				return err
			}
			file, err = os.Open(path)
		}
		return err
	}
	decode := json.NewDecoder(file)
	return decode.Decode(&s)
}

func (s *softwareSetting) save() error {
	s.SourcePath = sourcePathEntry.Text
	s.TargetPath = targetPathEntry.Text
	if outputNameFormatEntry.Text == "" {
		outputNameFormatEntry.SetText("$[page]-$[part]")
	}
	s.OutputNameFormat = outputNameFormatEntry.Text
	return s.saveToFile()
}

func (s *softwareSetting) saveToFile() error {
	err := os.MkdirAll(filepath.Dir(cp), 0700)
	if err != nil { // this is not an exists error according to docs
		return err
	}

	data, err := json.Marshal(&s)
	if err != nil {
		return err
	}

	return ioutil.WriteFile(cp, data, 0644)
}

func NewSoftwareSetting() *softwareSetting {
	cp = "config.json"
	s := &softwareSetting{}
	s.load()
	return s
}

func (s *softwareSetting) getFolderPath(win fyne.Window, callback func(str string)) func() {
	return func() {
		folderDialog := dialog.NewFolderOpen(func(list fyne.ListableURI, err error) {
			if list != nil {
				callback(list.Path())
			}
		}, win)
		folderDialog.SetConfirmText("选择")
		folderDialog.SetDismissText("取消")
		testData, _ := filepath.Abs("")
		dir, err := storage.ListerForURI(storage.NewFileURI(testData))
		if err != nil {
			return
		}
		folderDialog.SetLocation(dir)
		folderDialog.Show()
	}
}

func (s *softwareSetting) GetMenu(win fyne.Window) fyne.CanvasObject {
	sourcePathEntry = widget.NewEntry()
	sourcePathLabel := widget.NewLabel("缓存路径")
	sourcePathSetting := container.NewBorder(nil, nil, nil, widget.NewButton("修改", s.getFolderPath(win, func(str string) {
		sourcePathEntry.SetText(str)
		sourcePathEntry.Refresh()
		s.SourcePath = str
	})), sourcePathEntry)

	targetPathEntry = widget.NewEntry()
	targetPathLabel := widget.NewLabel("保存路径")
	targetPathSetting := container.NewBorder(nil, nil, nil, widget.NewButton("修改", s.getFolderPath(win, func(str string) {
		targetPathEntry.SetText(str)
		targetPathEntry.Refresh()
		s.TargetPath = str
	})), targetPathEntry)

	outputNameFormatEntry = widget.NewEntry()
	outputNameFormatEntry.SetPlaceHolder("$[page]-$[part]")
	outputNameFormatLabel := widget.NewLabel("保存名称")
	temp := widget.NewLabel("变量格式（默认为$[page]-$[part]）：\r\n$[page]：集数\t\t$[part]：分集名称\t\t$[title]：视频名称")
	outputNameFormatSetting := container.NewBorder(nil, temp, nil, nil, outputNameFormatEntry)

	onlyAudioLabel := widget.NewLabel("仅提取音频文件")
	onlyAudioCheck = widget.NewCheck("(启用后将不处理视频文件，【视频输出格式】配置项将失效)", func(b bool) {
		s.OnlyAudio = b
		if b {
			outputFormatRadioGroup.Disable()
			outputAudioFormatRadioGroup.Enable()
		} else {
			outputFormatRadioGroup.Enable()
			outputAudioFormatRadioGroup.Disable()
		}
	})

	outputFormatLabel := widget.NewLabel("视频输出格式")
	outputFormatRadioGroup = widget.NewSelect([]string{"mp4", "avi", "mkv", "mov"}, func(value string) {
		if value != "" {
			s.OutputFormat = value
		}
	})

	outputFormat := container.NewBorder(nil, nil, outputFormatRadioGroup, nil, nil)

	outputAudioFormatLabel := widget.NewLabel("音频输出格式")
	outputAudioFormatRadioGroup = widget.NewSelect([]string{"mp3", "aac", "wav", "flac", "ogg", "wma"}, func(value string) {
		if value != "" {
			s.OutputAudioFormat = value
		}
	})
	outputAudioFormat := container.NewBorder(nil, nil, outputAudioFormatRadioGroup, nil, nil)

	overrideOutputLabel := widget.NewLabel("是否覆盖保存")
	overrideOutputCheck = widget.NewCheck("(启用后会覆盖上次转换完成的视频)", func(b bool) {
		s.OverrideOutput = b
	})

	sourceClearFlagLabel := widget.NewLabel("是否清除源文件")
	sourceClearFlagCheck = widget.NewCheck("(转换完成后将删除缓存的源文件)", func(b bool) {
		s.SourceClearFlag = b
	})

	bitrateLabel := widget.NewLabel("视频码率压缩")
	bitrateEntry = widget.NewEntry()

	bitrateSlider := widget.NewSlider(1, 100)
	bitrateSlider.Step = 1
	bitrateSlider.OnChanged = func(value float64) {
		//bitrateValue.SetText(fmt.Sprintf("%.0f%%", value))
		tempValue, err := strconv.ParseInt(fmt.Sprintf("%.0f", value), 10, 64)
		if err == nil {
			// 保存该值(码率)
			s.Bitrate = tempValue
			bitrateEntry.SetText(strconv.FormatInt(s.Bitrate, 10))
		}
	}
	bitrateEntry.Validator = func(text string) error {
		for _, char := range text {
			if char < '0' || char > '9' {
				return fmt.Errorf("fail!")
			}
		}
		tempValue, err := strconv.ParseInt(text, 10, 64)
		if err == nil {
			// 判断边界
			if tempValue < 1 {
				tempValue = 1
			} else if tempValue > 100 {
				tempValue = 100
			}
			// 保存该值(码率)
			s.Bitrate = tempValue
			bitrateSlider.SetValue(float64(s.Bitrate))
			return nil
		} else {
			return err
		}
	}

	bitrate := container.NewBorder(nil, nil, nil, bitrateEntry, bitrateSlider)

	diskProtectEntry = widget.NewEntry()
	diskProtectEntry.Resize(fyne.NewSize(70, 32))
	diskProtectEntry.Validator = func(text string) error {
		for _, char := range text {
			if char < '0' || char > '9' {
				return fmt.Errorf("fail!")
			}
		}
		tempValue, err := strconv.ParseInt(text, 10, 64)
		if err == nil {
			// 判断边界
			if tempValue <= 0 {
				tempValue = -1
			}
			// 保存该值(码率)
			s.DiskProtect = tempValue
			return nil
		} else {
			return err
		}
	}
	diskProtectLabel := widget.NewLabel("磁盘空间保护")
	spaceTypeLabel := widget.NewLabel("G（请填写整数，最小值为1）")
	spaceTypeLabel.Move(fyne.NewPos(70, 0))
	diskProtect := container.NewWithoutLayout(diskProtectEntry, spaceTypeLabel)

	blank := widget.NewLabel("         ")
	blankValue := widget.NewLabel("                                    ")
	settingGroup := container.New(
		layout.NewFormLayout(),
		sourcePathLabel,
		sourcePathSetting,
		targetPathLabel,
		targetPathSetting,
		outputNameFormatLabel,
		outputNameFormatSetting,
		diskProtectLabel,
		diskProtect,
		onlyAudioLabel,
		onlyAudioCheck,
		outputFormatLabel,
		outputFormat,
		outputAudioFormatLabel,
		outputAudioFormat,
		overrideOutputLabel,
		overrideOutputCheck,
		sourceClearFlagLabel,
		sourceClearFlagCheck,
		bitrateLabel,
		bitrate,
		blank,
		blankValue)
	btn := container.NewBorder(nil, nil, layout.NewSpacer(), layout.NewSpacer(), widget.NewButton("保存", func() {
		err := s.save()
		if err != nil {
			return
		}
		s.load()
		win.Close()
	}))
	cnt := container.NewVBox(settingGroup, btn)
	s.load()
	return cnt
}
